/******************************************************************
 * This program was written as part of
 *   Assignment 4
 *   Computer Graphics                                             
 *   Semester 1, 2013
 *   School of Computer Science,
 *   The University of Adelaide
 *                                                                          
 * I declare that all material in this assessment is my own work except where
 * there is clear acknowledgement and reference to the work of others. I have
 * read the University Policy Statement on Plagiarism, Collusion and Related
 * Forms of Cheating:  

 * http://www.adelaide.edu.au/policies/?230

 * I give permission for my assessment work, including my subversion
 * repository, to be reproduced and submitted to academic staff for the
 * purposes of assessment and to be copied, submitted and retained in a form
 * suitable for electronic checking of plagiarism.  
                            
 * Authors:
 *  Terry Moschou    a1208955
 *  Salih Al-Rashid  a1210108
 *  Ben McAleer      a1212203
*******************************************************************/
#include "Skybox.hpp"
#include "Model.hpp"
#include "Context.hpp"
#include "Scene.hpp"
#include "objLoader.h"
#include "Material.hpp"

SkyBox::SkyBox(Object * parent /* = 0 */,
               glm::mat4 skyBoxMatrix /* = glm::mat4(1.0f) */) 
  : Object(parent, skyBoxMatrix, 0, "Skybox")
{
  
  Program * program = g_context.useProgram(Context::SkyBox);
  
  m_al_vertex   = program->getAttributeLocation("VertexPosition");
  m_al_texCoord = program->getAttributeLocation("VertexTexCoord");
  
  m_ul_model    = program->getUniformLocation("MatrixModel");
  m_ul_texMap   = program->getUniformLocation("TexMap");
  
  
  m_objLoader = new objLoader();
  m_objLoader->load("resources/models/skybox.obj");
    
  GLfloat * vertices  = new GLfloat[m_objLoader->faceCount * 3 * 3];
  GLfloat * texCoords = new GLfloat[m_objLoader->faceCount * 3 * 2];
  
  m_materialCount = m_objLoader->materialCount;
  m_materials = static_cast<Material*> ( // allocate but dont construct
      ::operator new (sizeof(Material[m_materialCount]))
    );
  
  GLfloat * vp = vertices;
  GLfloat * tp = texCoords;
 
  
  
  int offset = 0;
  for (int m = 0; m < m_objLoader->materialCount; ++m) {
    obj_material * material = m_objLoader->materialList[m];
    new (&m_materials[m]) Material(material);
    m_materials[m].m_offset = offset;
    int vertexCount = 0;
    
    for (int f = 0; f < m_objLoader->faceCount; ++f) {
      obj_face * face = m_objLoader->faceList[f];
 
      if (face->material_index != m)
        continue;
      for (int i = 0; i < 3; ++i) {
        offset++;
        vertexCount++;
        
        int vi = face->vertex_index[i];
        *vp++ = m_objLoader->vertexList[vi]->e[0];
        *vp++ = m_objLoader->vertexList[vi]->e[1];
        *vp++ = m_objLoader->vertexList[vi]->e[2];
       
        int ti = face->texture_index[i];
        if (ti >= 0) {
          *tp++ = m_objLoader->textureList[ti]->e[0];
          *tp++ = m_objLoader->textureList[ti]->e[1];
        } else {
          *tp++ = 0.0f;
          *tp++ = 0.0f;
        }
        
      }    
    }
    m_materials[m].m_vertexCount = vertexCount;
  }
  
  glGenVertexArrays(1, &m_vaoHandle);
  glBindVertexArray(m_vaoHandle);
  
  glGenBuffers(NumVbos, m_vboHandles);
    
  glBindBuffer(GL_ARRAY_BUFFER, m_vboHandles[Vertices]);
  glBufferData(GL_ARRAY_BUFFER,
               m_objLoader->faceCount * 3 * 3 * sizeof(GLfloat),
               vertices, GL_STATIC_DRAW);
  glEnableVertexAttribArray(m_al_vertex);
  glVertexAttribPointer(m_al_vertex,
                        3, GL_FLOAT, GL_FALSE, 0, 0);
  
  glBindBuffer(GL_ARRAY_BUFFER, m_vboHandles[TexCoords]);
  glBufferData(GL_ARRAY_BUFFER,
               m_objLoader->faceCount * 3 * 3 * sizeof(GLfloat),
               texCoords, GL_STATIC_DRAW);
  glEnableVertexAttribArray(m_al_texCoord);
  glVertexAttribPointer(m_al_texCoord,
                        2, GL_FLOAT, GL_FALSE, 0, 0);
  
  glBindBuffer(GL_ARRAY_BUFFER, 0);
  glBindVertexArray(0);

}

void SkyBox::draw(const glm::mat4 & modelMatrix)
{
  
  Program * program = g_context.useProgram(Context::SkyBox);
  
  glm::mat4 newModelMatrix = modelMatrix * m_modelMatrix;
  glUniformMatrix4fv(m_ul_model, 1, false,
                       &newModelMatrix[0][0]);
  
  glBindVertexArray(m_vaoHandle);  
  
  for (int m = 0; m < m_materialCount; ++m) {
    if (m_materials[m].m_hasTexture) {
      glBindTexture(GL_TEXTURE_2D, m_materials[m].m_tboHandle);
    } else {
    }
    glDrawArrays(GL_TRIANGLES, m_materials[m].m_offset,
                 m_materials[m].m_vertexCount);
  }
  glBindTexture(GL_TEXTURE_2D, 0);
  glBindVertexArray(0);
  
}
